package cn.edu.zut.internship.web.utils;


import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    // =============================common============================
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                long rawTimeout = TimeoutUtils.toMillis(time, TimeUnit.SECONDS);
                byte[] rawKey = serializeObj(key);
                try {
                    return getConnection().pExpire(rawKey, rawTimeout);
                }catch (Exception var8) {
                    return getConnection().expire(rawKey, TimeoutUtils.toSeconds(time, TimeUnit.SECONDS));
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        byte[] rawKey = serializeObj(key);
        try {
            return getConnection().pTtl(rawKey, TimeUnit.SECONDS);
        } catch (Exception var4) {
            return getConnection().ttl(rawKey, TimeUnit.SECONDS);
        }
    }

    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        byte[] rawKey = serializeObj(key);

        try {
            return getConnection().exists(rawKey);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public Long del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                byte[] rawKey = serializeObj(key[0]);
                Long result = getConnection().del(new byte[][]{rawKey});
                return  result!=null?result:0L;
            } else {
                //String数组转List
                List list = CollectionUtils.arrayToList(key);
                //list转换为byte[][]二维数组
                byte[][] rawKeys = new byte[list.size()][];
                int i = 0;
                Object next;
                for(Iterator var4 = list.iterator(); var4.hasNext(); rawKeys[i++] = serializeObj(next)) {
                    next = var4.next();
                }
                Long result = getConnection().del(rawKeys);
                return result!=null?result:0L;
            }
        }
        return 0L;
    }

    /**
     * 删除Set集合内所有Key的缓存
     * @param keys
     */
    public Long delSet(Set keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return 0L;
        } else {
            //set转换为byte[][]二维数组
            byte[][] rawKeys = new byte[keys.size()][];
            int i = 0;
            Object next;
            for(Iterator var4 = keys.iterator(); var4.hasNext(); rawKeys[i++] = serializeObj(next)) {
                next = var4.next();
            }
            Long result = getConnection().del(rawKeys);
            return  result!=null?result:0L;
        }
    }


    /**
     * 查找所有符合给定模式 pattern 的 key 。。
     * @param pattern
     * @return
     */
    public Set keys(String  pattern) {
        Set<byte[]> set = getConnection().keys(serializeObj(pattern));
        Set<String> keys = new HashSet<>();
        for(byte[] one:set){
            keys.add((String) deserializeObj(one));
        }
        return keys;
    }

    // ============================String=============================
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        byte[] rawKey = serializeObj(key);
        return getConnection().incrBy(rawKey, delta);
    }

    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        byte[] rawKey = serializeObj(key);
        return getConnection().incrBy(rawKey, -delta);
    }
    /**
     * 序列化
     * @param object
     * @return
     */
    private static byte[] serializeObj(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {
            throw new RuntimeException("序列化失败!", e);
        }
    }

    /**
     * 反序列化
     * @param bytes
     * @return
     */
    private static Object deserializeObj(byte[] bytes) {
        if (bytes == null){
            return null;
        }
        ByteArrayInputStream bais = null;
        try {
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException("反序列化失败!", e);
        }
    }

    private RedisConnection getConnection(){
        return  redisTemplate.getConnectionFactory().getConnection();
    }
}
